package com.google.gwt.sample.contacts.client.contacts;

import com.google.gwt.sample.contacts.client.action.DeleteContactAction;
import com.google.gwt.sample.contacts.client.action.DeleteContactResponse;
import com.google.gwt.sample.contacts.client.action.GetContactDetailsAction;
import com.google.gwt.sample.contacts.client.action.GetContactDetailsResponse;
import com.google.gwt.sample.contacts.client.alerter.Alerter;
import com.google.gwt.sample.contacts.client.common.ColumnDefinition;
import com.google.gwt.sample.contacts.client.common.ContactsColumnDefinitionsImpl;
import com.google.gwt.sample.contacts.client.common.SelectionModel;
import com.google.gwt.sample.contacts.client.contacts.ContactsPresenter.ContactsBinder;
import com.google.gwt.sample.contacts.client.editcontact.EditContactPresenter;
import com.google.gwt.sample.contacts.client.hascontent.HasContent;
import com.google.gwt.sample.contacts.shared.ContactDetails;
import com.google.gwt.user.client.ui.HasHTML;
import com.google.inject.Inject;

import com.guit.client.GuitPresenter;
import com.guit.client.async.AbstractAsyncCallback;
import com.guit.client.async.event.AsyncExceptionEvent;
import com.guit.client.binder.Attribute;
import com.guit.client.binder.EventBusHandler;
import com.guit.client.binder.GuitBinder;
import com.guit.client.binder.ViewField;
import com.guit.client.binder.ViewHandler;
import com.guit.client.binder.contributor.Debug;
import com.guit.client.binder.contributor.HasAttribute;
import com.guit.client.place.Place;

import java.util.List;

public class ContactsPresenter extends GuitPresenter<ContactsBinder> implements Place<Void> {

    public static enum Actions {
        SELECT, CLICK
    }

    public static interface ContactsBinder extends GuitBinder<ContactsPresenter> {
    }
    
    @ViewField
    HasHTML contactsTable;

    protected List<ContactDetails> contactDetails;
    protected final SelectionModel<ContactDetails> selectionModel = new SelectionModel<ContactDetails>();
    private final HasContent hasContent;
    private final List<ColumnDefinition<ContactDetails>> columnDefinitions = ContactsColumnDefinitionsImpl.getInstance();
    private final Alerter alerter;

    @Inject
    public ContactsPresenter(HasContent hasContent, Alerter alerter) {
        this.hasContent = hasContent;
        this.alerter = alerter;
    }

    @ViewHandler
    protected void addButton$click() {
        placeManager.go(EditContactPresenter.class);
    }

    @ViewHandler
    protected void deleteButton$click() {
        List<ContactDetails> selectedContacts = selectionModel.getSelectedItems();

        int size = selectedContacts.size();
        String[] ids = new String[size];
        for (int i = 0; i < size; ++i) {
            ids[i] = selectedContacts.get(i).getId();
        }

        commandService.execute(new DeleteContactAction(ids), new AbstractAsyncCallback<DeleteContactResponse>() {
            @Override
            public void success(DeleteContactResponse result) {
                contactDetails = result.getContacts();
                sortContactDetails();
                setRowData(contactDetails);
            }
        });
    }

    @ViewHandler
    @HasAttribute({"action", "index"})
    @Debug
    protected void contactsTable$click(@Attribute Actions action, @Attribute Integer index) {
        ContactDetails contactDetail = contactDetails.get(index);
        if (action.equals(Actions.CLICK)) {
            placeManager.go(EditContactPresenter.class, contactDetail.getId());
        } else {
            if (selectionModel.isSelected(contactDetail)) {
                selectionModel.removeSelection(contactDetail);
            } else {
                selectionModel.addSelection(contactDetail);
            }
        }
    }

    public void sortContactDetails() {
        for (int i = 0; i < contactDetails.size(); ++i) {
            for (int j = 0; j < contactDetails.size() - 1; ++j) {
                if (contactDetails.get(j).getDisplayName().compareToIgnoreCase(contactDetails.get(j + 1).getDisplayName()) >= 0) {
                    ContactDetails tmp = contactDetails.get(j);
                    contactDetails.set(j, contactDetails.get(j + 1));
                    contactDetails.set(j + 1, tmp);
                }
            }
        }
    }

    protected void fetchContactDetails() {
        commandService.execute(new GetContactDetailsAction(), new AbstractAsyncCallback<GetContactDetailsResponse>() {
            @Override
            public void success(GetContactDetailsResponse result) {
                contactDetails = result.getContacts();
                sortContactDetails();
                setRowData(contactDetails);
            }
        });
    }
    
    public void setRowData(List<ContactDetails> rowData) {
        StringBuilder sb = new StringBuilder();
        sb.append("<table><tbody>");
        for (int i = 0; i < rowData.size(); ++i) {
            ContactDetails t = rowData.get(i);
            sb.append("<tr>");
            for (int j = 0; j < columnDefinitions.size(); ++j) {
                sb.append("<td>");
                columnDefinitions.get(j).render(t, sb, i);
                sb.append("</td>");
            }
            sb.append("</tr>");
        }
        
        sb.append("</tbody></table>");

        contactsTable.setHTML(sb.toString());
    }

    @Override
    public void go(Void data) {
        fetchContactDetails();
        hasContent.setContent(getView());
    }

    /**
     * Subscribe to any async exception.
     */
    @EventBusHandler(AsyncExceptionEvent.class)
    protected void $asyncException(Throwable caught) {
        alerter.alert("Error contacting to the server: " + caught.toString());
    }
}
